#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@NAME          : pptxact.py
@TIME          : 2024/11/18 22:12:33
@AUTHOR        : chenlip
@VERSION       : 0.0.1
@DESCRIPTION   : pptx文件操作
'''
# --------------------------引用--------------------------
import os, sys, json, copy
from .utils import Utils as ut
from pptx import Presentation, presentation, slide, shapes
from pptx.util import Inches, Pt, Cm

import comtypes.client
# --------------------------引用--------------------------

'''
    这是一个PPTX文件操作的类,用于创建PPt文件,并且可以插入ppt的新页面、在ppt页面上可以插入图片,表格,文本等
    名词：
        - slide: ppt中的一页
        - shape: ppt中的元素，如文本框、图片、表格等
        - placeholder: 占位符，ppt中的元素，如标题、正文、日期等
        - text_frame: 文本框，shape中的文本框
        - paragraph: 段落，文本框中的段落
        - run: 文本，段落中的文本
    方法：
        Inches: 英寸，单位,
        Pt: 点，单位, 1pt=1/72英寸, 1英寸=2.54厘米, 一般用于设置字体大小
        Cm: 厘米，单位, 一般用于设置shape的大小和位置

    占位符类型：
        - shapes.placeholders[0]: 0标识页面内占位符的索引

    参考：
        https://zhuanlan.zhihu.com/p/291729098
        https://zhuanlan.zhihu.com/p/265464562
'''

class PPTXAct:
    
    def __init__(self, basepath:str=None):
        '''
            初始化方法，初始化PPTXAct类，指定PPT文件的处理路径
            :Args:
                - basepath: ppt文件的处理路径

        '''
        _pptxpath = os.path.join(ut.APPLICATION_ROOT, 'pptx')
        self.basepath = basepath
        if basepath is None:
            if not os.path.exists(_pptxpath): os.mkdir(_pptxpath)
            self.basepath = _pptxpath

        # pptx文件导出的图片路径
        self.pngpath = os.path.join(self.basepath, 'png')
        if not os.path.exists(self.pngpath): os.mkdir(self.pngpath)
        
    def _slide_to_image(self, pptxfile, slide_index, output_path):
        '''
            将单个幻灯片转换为图像
            :Args:
                - pptxfile: pptx文件的路径
                - slide_index: 幻灯片索引
                - output_path: 输出图像路径
        '''
        powerpoint = comtypes.client.CreateObject("PowerPoint.Application")
        powerpoint.Visible = 1
        powerpoint.DisplayAlerts = 0  # 不显示，不警告
        # powerpoint.WindowState = 2  # ppWindowMinimized

        presentation = powerpoint.Presentations.Open(pptxfile)
        slide = presentation.Slides[slide_index + 1]  # Slide index is 1-based in PowerPoint
        # 我希望输出1242*1660的图片
        slide.Export(output_path, "PNG")
        presentation.Close()
        powerpoint.Quit()

    def createpptx(self, targetfname:str, templatefname:str=None):
        '''
            创建一个pptx文件,
            :Args:
                - targetfname: 目标文件名，例如：'test.pptx'
                - templatefname: 模板文件名. 如果没有模板文件,则创建一个空白的pptx文件
            :Return:
                - pptx文件的路径
        '''
        pptxfile = os.path.join(self.basepath, targetfname)
        if os.path.exists(pptxfile): os.remove(pptxfile)
        if templatefname is None:
            prs = Presentation()
            prs.save(pptxfile)
        else:
            prs = Presentation(templatefname)
            prs.save(pptxfile)
        
        return pptxfile

    # createpptx

    # 填充封面内容
    def fillcover(self, pptxfile:str, content:dict):
        '''
            填充pptx文件的封面内容
            :Args:
                - pptxfile: pptx文件的路径
                - content: 填充的内容
                    {
                        'subject': '主标题',
                        'secsubject': '副标题', # 副标题
                        'content': '内容',
                    }
        '''
        self.fillpage(pptxfile, 0, content)

    # 填充某一页的内容
    def fillpage(self, pptxfile:str, pageidx:int, content:dict):
        '''
            填充pptx文件的某一页的内容
            :Args:
                - pptxfile: pptx文件的路径
                - pageidx: pptx文件的页码
                - content: 填充的内容
                    {
                        'subject': '主标题',
                        'secsubject': '副标题', # 副标题
                        'subject1': {
                            'title': '标题1',
                            'content': '内容1'
                        },
                    }
        '''
        prs = Presentation(pptxfile)
        slide = prs.slides[pageidx]
        for shape in slide.shapes:
            if shape.has_text_frame:
                for paragraph in shape.text_frame.paragraphs:
                    for run in paragraph.runs:
                        if run.text in content:
                            run.text = content[run.text]
        
        prs.save(pptxfile)

    # 将某个PPT文件每一页转成高清PNG图片
    def ppt2png(self, pptxfile:str, output_dir:str=None) -> list:
        '''
            将pptx文件的每一页转成高清PNG图片
            :Args:
                - pptxfile: pptx文件的路径
                - output_dir: 输出图片的目录
            :Return:
                - 图片路径的文件列表
        '''
        _fname = os.path.basename(pptxfile).split('.')[0]
        if output_dir is None:
            output_dir = self.pngpath

        powerpoint = comtypes.client.CreateObject("PowerPoint.Application")
        powerpoint.Visible = 1  # 设置为后台运行
        # powerpoint.WindowState = 2  # ppWindowMinimized

        presentation = powerpoint.Presentations.Open(pptxfile, WithWindow=False)
        
        # 设置幻灯片尺寸为 1242x1660 像素
        # presentation.PageSetup.SlideWidth = 1242 * 0.75  # 转换为磅（1 像素 = 0.75 磅）
        # presentation.PageSetup.SlideHeight = 1660 * 0.75  # 转换为磅（1 像素 = 0.75 磅）

        _pngfiles = []
        for idx, slide in enumerate(presentation.Slides):
            slidefile = os.path.join(output_dir, f'{_fname}-{idx + 1}.png')
            slide.Export(slidefile, "PNG")
            _pngfiles.append(slidefile)

        presentation.Close()
        powerpoint.Quit()

        return _pngfiles
    
    # 打印pptx文件的内容，某一页中所有的shape,并判断shape中是否有文本内容，如果有打印出shape中的文本内容
    def printpptx(self, pptxfile:str):
        '''
            打印pptx文件的内容，某一页中所有的shape,并判断shape中是否有文本内容，如果有打印出shape中的文本内容
            :Args:
                - pptxfile: pptx文件的路径
        '''
        prs = Presentation(pptxfile)
        _i = 0
        for idx, slide in enumerate(prs.slides):
            print(ut.cco(f'--- 第{idx}页: 文本内容 ', 'green'))
            if _i > 3: break # 只打印前3页的内容
            for shape in slide.shapes:
                if shape.has_text_frame:
                    text_frame = shape.text_frame
                    print(ut.cco("shape.text_frame== ", "blue") ,text_frame.text)

                    # for paragraph in shape.text_frame.paragraphs:
                    #     for run in paragraph.runs:
                    #         print(run.text)
            
            # 输出页面中的占位符ID
            print(ut.cco(f'== 第{idx}页的占位符ID:', 'green'))
            for ph in slide.placeholders:
                phf = ph.placeholder_format
                print(f"{phf.idx}---{phf.type}")
            
            _i += 1

    # 复制ppt某一页的内容为新的一页
    def cp_slide2new(self, pres:presentation, slide_index:int, copy_times:int):
        '''
            复制ppt某一页的内容为新的一页多次
            :Args:
                - pres: Presentation对象
                - slide_index: 要复制的幻灯片索引（从0开始）
                - copy_times: 复制次数
        '''

        source_slide = pres.slides[slide_index]

        for _ in range(copy_times):
            # 使用源幻灯片的布局添加新幻灯片
            slide_layout = source_slide.slide_layout
            new_slide = pres.slides.add_slide(slide_layout)

            # 复制形状
            for shape in source_slide.shapes:
                new_element = copy.deepcopy(shape.element)
                new_slide.shapes._spTree.insert_element_before(new_element, 'p:extLst')

            # 复制幻灯片级关系
            for rel in source_slide.part.rels:
                if "notesSlide" not in rel.reltype:
                    new_slide.part.rels.add_relationship(rel.reltype, rel._target, rel.rId)

    # 新增一个slide, 并使用母版页中的板式应用到新增的幻灯片slide
    def apply_slide_layout(self, prs:presentation, master_layout_inx:int=1):
        '''
            使用母版页中的板式应用到新增的幻灯片slide
            :Args:
                - master_layout_inx: 母版页中版式索引
        '''

        # 获取母版并选择一个布局
        slide_master = prs.slide_master
        custom_layout = slide_master.slide_layouts[master_layout_inx]  # 使用母版中的第二个布局

        # 添加幻灯片并应用布局
        slide = prs.slides.add_slide(custom_layout)

        return slide


        
        
        
